CubeOps v2.11
a D2Mod plugin by Myhrginoc
portions by Havvoric, VillageIdiot and kingpin


The plugin uses the D2Mod System by SVR and will work ONLY with Diablo II v1.10 final release.

The plugin allows the mod maker to control whether or not cube recipes are available according to character
and item properties.  With Diablo II v1.10, three new columns were introduced to Cubemain.txt: Op, Stat and
Value.  Blizzard defined 28 op codes to provide conditional activation of recipes up front.  This plugin adds
functionality to 24 of these codes, and adds an additional 26 codes with new conditional characteristics. And
with v2.x, the plugin also uses the Enable field as a mode switch for much greater flexibility.

Recipes can be restricted to hardcore games only or softcore games only.

The Ethereal property works to clear as well as set an item's ethereal status.

See the Master Table below for the complete op code list.  Expanded op codes are 3 through 26.  New op codes
start at 29.

Havvoric contributed code for SetStat and IncStat.  These were released as a separate plugin, but there were
conflicts with CubeOps.  This release removes the multiplayer glitches from the originals.  It was easier to
merge the two functions into this plugin than to revise both plugins.

Havvoric contributed code for Inscribe.  Although no conflicts with CubeOps existed, better to kill two birds
with one stone.  One less plugin to include in your mod!

VillageIdiot developed the method for creating red portals from cube recipes, with a little help from kingpin.

VillageIdiot found the location to disable the Cow King check when creating portals to the Cow Level.

Future versions of this plugin will contain additional op codes.  There are 255 possible (0 being skip ops).
In addition, using the Enable field as a mode switch allows even more complex recipe filters.  Feel free to
suggest more codes in the D2Mod and PlugY Forum and I will evaluate them for technical feasibility.  (The Enable
table below includes a number of reserved filters which will be implemented in future plugin versions.)

I am trying to implementing SVR's socket remover and stat transfer features, but these are not working with the
other parts of this plugin yet.  They will be available with v3.0, along with some other ideas in the queue.


Activating the Plugin
=====================

This plugin uses the D2Mod system to load into a game.  In the [D2MOD] section of d2mod.ini, add the line

	CubeOps=CubeOps.dll

A successful load will be recorded in your debug file.  Near the top you should see lines like these:

10:42:23.680  imagehlp.dll loaded for better stack crawls.
10:42:30.430  CubeOps.dll loaded at 6FD60000			<--- CubeOps is active!
10:42:30.508  Opening GDI window at 640x480...
10:42:30.508  Low End Machine: FALSE
10:42:30.508  D2CMPSpriteCacheInit: 512000

The section of Properties.txt included in this plugin should be appended to your Properties.txt file, if you want
to use the Inscribe, SetStat, IncStat and SetMinLevel features.

>>> IMPORTANT NOTE: Do not use this plugin with the existing Inscribe and SetStat plugins.  Do not use with other
                    code edits that disable the Cow King check or implement Red Portals.


Enabling Cube Recipes
=====================

You must edit Cubemain.txt to remove all non-zero values from the Enable and Version columns.  The Enable field is
now a mode switch, with 0 being the default behavior when enable was 1 before.  As Enable is a byte field, simply
subtract 1 from all current values (-1 is the same as 255).  The Version field is now another parameter for op codes,
with LoD/non-LoD control moved to the Enable field as a special mode.  To preserve existing Cubemain behavior, set
Enable to 254 (or -2) where Version is now 0, and set Enable to 0 where Version is now 100.

The Enable Field Mode Switches
------------------------------

 Recipe works in either Hardcore or Softcore games
    0 - Default behavior, corresponds to original value 1, op codes apply to all input items
    1 -   (reserved for future use)
    2 -   (reserved for future use)
    3 -   (reserved for future use)
    4 -   (reserved for future use)
    5 -   (reserved for future use)
    6 -   (reserved for future use)
    7 -   (reserved for future use)
    8 - NumInputs forced to 2, NumInputs field can be used for special recipes as another parameter
    9 - NumInputs forced to 3, NumInputs field can be used for special recipes as another parameter
   10 - NumInputs forced to 4, NumInputs field can be used for special recipes as another parameter
   11 - NumInputs forced to 5, NumInputs field can be used for special recipes as another parameter
   12 - NumInputs forced to 6, NumInputs field can be used for special recipes as another parameter
   13 - NumInputs forced to 7, NumInputs field can be used for special recipes as another parameter

 Recipe works in Hardcore games only
   40 - Default behavior, corresponds to original value 1, op codes apply to all input items
   41 -   (reserved for future use)
   42 -   (reserved for future use)
   43 -   (reserved for future use)
   44 -   (reserved for future use)
   45 -   (reserved for future use)
   46 -   (reserved for future use)
   47 -   (reserved for future use)
   48 - NumInputs forced to 2, NumInputs field can be used for special recipes as another parameter
   49 - NumInputs forced to 3, NumInputs field can be used for special recipes as another parameter
   50 - NumInputs forced to 4, NumInputs field can be used for special recipes as another parameter
   51 - NumInputs forced to 5, NumInputs field can be used for special recipes as another parameter
   52 - NumInputs forced to 6, NumInputs field can be used for special recipes as another parameter
   53 - NumInputs forced to 7, NumInputs field can be used for special recipes as another parameter

 Recipe works in Softcore games only
   80 - Default behavior, corresponds to original value 1, op codes apply to all input items
   81 -   (reserved for future use)
   82 -   (reserved for future use)
   83 -   (reserved for future use)
   84 -   (reserved for future use)
   85 -   (reserved for future use)
   86 -   (reserved for future use)
   87 -   (reserved for future use)
   88 - NumInputs forced to 2, NumInputs field can be used for special recipes as another parameter
   89 - NumInputs forced to 3, NumInputs field can be used for special recipes as another parameter
   90 - NumInputs forced to 4, NumInputs field can be used for special recipes as another parameter
   91 - NumInputs forced to 5, NumInputs field can be used for special recipes as another parameter
   92 - NumInputs forced to 6, NumInputs field can be used for special recipes as another parameter
   93 - NumInputs forced to 7, NumInputs field can be used for special recipes as another parameter

 special codes
  252 - Recipe also applies to non-LoD games (Hardcore only)
  253 - Recipe also applies to non-LoD games (Softcore only)
  254 - Recipe also applies to non-LoD games (Either Hardcore or Softcore)
  255 - Recipe disabled, corresponds to original value 0 (useful for testing)

Any value not listed above will disable the recipe.  Reserved values will behave like Enable = 0.  As of this
release, no recipe function uses the additional parameter made possible by forcing NumInputs.  So modes 8-13,
48-53 and 88-93 have no useful effect beyond modes 0, 40 and 80 respectively.


Ethereal Items
==============
The "ethereal" property has been expanded into a switch.  The switch uses either the mod X param field or both
the mod X min and mod X max fields.  A blank, zero or negative value in these fields clears ethereality, a positive
value sets ethereality.  Example to set using param:

    mod 1 = ethereal
    mod 1 param = 1

example to clear using min/max:

    mod 1 = ethereal
    mod 1 min = 0
    mod 1 max = 0

If param and min are blank, zero or negative, and max is positive, then ethereality is set.


Using Inscribe
==============
This feature was developed by Havvoric.  It will personalize your item with your character name.  Append the
"inscribe" line from the included Properties.txt segment into your Properties.txt file.  If you already have
an inscribe line, change the Set1 field to 1.

In your CubeMain file, put "inscribe" in one of the mod X fields and any value up to 100 in the corresponding
chance X field.  The param X, min X and max X fields are not used.  A possible recipe to inscribe any weapon all
the time would be:

    Input1 = weap
    Input2 = isc 
    Output = useitem (usetype here will reroll the item and personalize together)
    mod 1 = inscribe
    mod 1 chance = 100 (or leave blank)

If the recipe appears to work but the item is not personalized, three things may have happened.  First, the item
must be nameable, this is set in weapons.txt, armor.txt or misc.txt.  Items with complex, multi-line titles may
overwrite the personalization line.  Finally, if bad data gets to the personalization function, a line will be
written in the debug file: Attempted to inscribe a type # id # ptUnit.


Using SetStat and IncStat
=========================
These features were developed by Havvoric.  SetStat will make an existing or new character stat directly, with a
value defined within a range.  IncStat will increase an existing character stat directly, where the change in stat
value is defined within a range.  Append the "SetStat" and/or "IncStat" lines from the included Properties.txt
segment into your Properties.txt file.  If you already have a SetStat line, change the Set1 field to 2 and the
Func1 field to 25.  If you already have an IncStat line, change the Set1 field to 3 and the Func1 field to 25.

In your CubeMain file, put "SetStat" in one of the mod X fields and any value up to 100 in the corresponding
chance X field.  The param X field gets the index from ItemStatCost.txt for the stat you wish to set.  Min X and
Max X define a range of values for this stat.  See the discussion below about encoded stats for those stats that
have non-zero values in the Encode field of ItemStatCost.txt.  A possible recipe to set a base stat of 20 to 30
defense "iron skin" every time it is used (apply catalyst or reagent form, not both) would be:

    Input1 = {catalyst | reagent}
    Output = {useitem | code for junk item}
    mod 1 = SetStat
    mod 1 param = 31 (stat_armorclass)
    mod 1 min = 20
    mod 1 max = 30

IncStat works the same way.  If you replace SetStat with IncStat in the above recipe, you will increase a base
defense by 20 to 30 points with each application.

If either SetStat or IncStat appear to work, but no character stat has changed, you may have changed a stat which
is not displayed in the character screen.  Or the recipe itself may have tried to process bad data.  In the
latter case, you would see a line in your debug file: Attempted to set or change player stat # on a non-player.


Using MinLevel
==============

This is a new property that sets the item level requirement to a minimum amount.  The output item is created if the
recipe is successful, and its level requirement is calculated.  The result is compared to the value of MinLevel (a
random number between Min and Max if they are different), and adjusted upward as required.  Or you can use the Param
field instead of Min and Max if you have a single value.  To set a random minimum item level requirement between
L50 and L60, define the recipe mod X fields:

    mod 1 = MinLevel
    mod 1 min = 50
    mod 1 max = 60


Using the Version Field
=======================

This column now has a new function in the game.  The original op codes 3 through 26 only worked for stats with
param=0 (which is most of them).  But you could not use these functions to read any other parameter.  For example,
oskill is stat 97 and the skill ID number is the parameter.  So in order to read how many points you have in
Amplify Damage, you have to be able to query parameter 66.  But with Op/Param/Value there aren't enough fields,
so I converted Version to a parameter field.  To check if you have oskill +3 to Amplify Damage, enter

    Version = 66
    Op = (see table below)
    Param = 97 (stat_item_nonclassskill)
    Value = 3

Unfortunately Param in this usage means stat from ItemStatCost, and Version means ItemStatCost parameter.  Thank
Blizzard for inconsistency!

To check the item stat count, or to check the number of items inserted into a socketed item, put the threshold
number in the Value field.


Red Portals
===========
This feature was developed by VillageIdiot and kingpin.  Output must be "Red Portal" and the target level ID goes
in the lvl field (first output only).  For example, if you want to cast a portal to Tamoe Highland:

    Output = "Red Portal"
    lvl    = 7

The red portals can only access levels 1 through 255, if you add more levels with the ExtLvl portal you must use
other access methods.  Also the portals will only work for destination levels in the same act as your starting
level.


Cow Level
=========
The Cow Level recipe has two variations now.  The lvl field (first output only) now acts as a Cow King check:
0 if killing the Cow King prohibits further portal creation for that difficulty, 1 if killing the Cow King does
not matter.  You can even have two Cow Level recipes, one for limited access and one for unlimited access.  This
feature does not change the requirement to complete the difficulty before the Cow Level can be accessed.


Encoded Stats
=============

Some properties have complex definitions using bitmapped encodings.  Three encodings may be used here (the /bytime
encoding is not known or supported yet).  The encode value comes from ItemStatCost.txt.

Encode = 1	Used for skills primarily, this is described above in "Using the Version Field."  Both param and
		version allow 16-bit numbers, but practically you only have 512 values for stats (param), and the
		version field will be limited by the stat definition.

Encode = 2	Used for chance-to-cast skills, the Version field has several parts.  A number in this field must
                be bitmapped in the pattern aabbbbbbcccccccc, where skill level is bbbbbb (0 to 63) and skill ID
		is ccccccccaa (0 to 1023).  The Value field is the chance to cast in percent.  A possible recipe
		to check if you have 15% chance to cast L3 Amplify Damage on striking would be

			Version = 33552 (10.000011.00010000)
			Op = (see table below)
			Param = 198 (stat_item_skillonhit)
			Value = 15

Encode = 3	Used for charged skills, both Version and Value have bitmap patterns.  The Version pattern is the
		same as Encode=2.  The Value field is 0000000000000000aaaaaaaabbbbbbbb, where maximum charges is
                aaaaaaaa and current charges is bbbbbbbb; in each case the range is 0 to 255.  For a recipe to
		check for 2/3 full L3 Amplify Damage with 45 charges maximum, fill the fields as shown

			Version = 33552 (10.000011.00010000)
			Op = (see table below)
			Param = 204 (stat_item_charged_skill)
			Value = 11550 (45 max charges * 256 + 30 current charges)
                

Advisories
==========

Some properties have multiple stats (e.g. poison and cold damages, resist-all).  Each stat is counted separately
by the game, so an item with 12 properties visible might have over 20 stats from those properties.

LevelReq is computed each time, as it is a factor of the base item, applied values from SetItems or UniqueItems,
requirements for each affix, and results of crafting or level-setting cube mods.  You can use the item_levelreq
stat for an op filter, but an item may have requirements whether this stat is used or not, or the stat may be
present as one of several level-setting factors.  There are now eight filters designed to handle the dynamic
behavior of LevelReq.


        *****************************************
        *                                       *
        *   MASTER TABLE OF CUBEMAIN OP CODES   *
        *                                       *
        *****************************************

op      Skip recipe if ... 
-------------------------------------------------------- 
1       DayOfMonth is less than Param or greater than value 
2       DayOfWeek != value ( 1 = Sunday, ...) 


        Skip recipe on character if ... 
-------------------------------------------------------- 

~~~~~~ 10519 ~~~~~~~~~~
Stat.Accr
(Param in Version field)

3       player stat(param) < value 
4       player stat(param) > value 
5       player stat(param) = value 
6       player stat(param) ! value 


~~~~~~ 10521 ~~~~~~~~~
Stat.Base
(Param in Version field)

7       player stat(param) < value 
8       player stat(param) > value 
9       player stat(param) = value 
10      player stat(param) ! value 


~~~~~~ 10522 ~~~~~~~~~
Stat.Bonus  (accr - base)
(Param in Version field)

11      player stat(param) < value 
12      player stat(param) > value 
13      player stat(param) = value 
14      player stat(param) ! value 


        Skip recipe on item if ... 
-------------------------------------------------------- 

~~~~~~ 10519 ~~~~~~~~~ 
Stat.Accr
(Param in Version field)

15      item stat(param) < value 
16      item stat(param) > value 
17      item stat(param) = value 
18      item stat(param) ! value 


~~~~~~ 10521 ~~~~~~~~~ 
Stat.Base
(Param in Version field)

19      item stat(param) < value 
20      item stat(param) > value 
21      item stat(param) = value 
22      item stat(param) ! value 


~~~~~~ 10522 ~~~~~~~~~ 
Stat.Bonus  (accr - base)
(Param in Version field)

23      item stat(param) < value 
24      item stat(param) > value 
25      item stat(param) = value 
26      item stat(param) ! value 


27      pItem->OtherID != value 
28      pItem->QuestDiff < ptGame->Diff 
        (only checked if Quest and QuestDiffCheck are 1) 


* * *   NEW CODES by Myhrginoc   * * *

        Skip recipe on item if ... 
-------------------------------------------------------- 

29      item statcount(base) < value 
30      item statcount(base) > value 
31      item statcount(base) = value 
32      item statcount(base) ! value
 
33      item statcount(accr) < value 
34      item statcount(accr) > value 
35      item statcount(accr) = value 
36      item statcount(accr) ! value

37      item statcount(bonus) < value 
38      item statcount(bonus) > value 
39      item statcount(bonus) = value 
40      item statcount(bonus) ! value

41      item filledsockets < value 
42      item filledsockets > value 
43      item filledsockets = value 
44      item filledsockets ! value

45      item levelreq < value 
46      item levelreq > value 
47      item levelreq = value 
48      item levelreq ! value

49      item levelreq < player level 
50      item levelreq > player level 
51      item levelreq = player level 
52      item levelreq ! player level

53	item is not personalized
54	item is personalized


Revision History
================
v2.11   06/27/2007    Ethereal property can clear as well as set the ethereal condition.
v2.10   01/20/2007    Hardcore-only and softcore-only recipes possible.  Fix "mod" parameter bug.
v2.02   09/25/2006    Include VillageIdiot infinite Cow Level and Red Portal (with kingpin).
v2.01	07/09/2006    Fix bugs and add features, including Havvoric Inscribe, SetStat and IncStat.
v2.00   12/25/2005    Expanded op code list (45-54), new Enable field.
v1.03   10/11/2005    Initial release.

Acknowledgements
================
2005-2007 by Myhrginoc (portions by Havvoric, VillageIdiot and kingpin)
D2Mod system by SVR
Diablo, Diablo II are 1996-2007 Blizzard Entertainment
Definitions of Blizzard op codes discovered by SVR and Joel, initial table by SVR